Tranport Modal Share for Dublin City Centre

Introduction

This notebook is intended as a brief analysis of the Central Statistics Office “Workplace Zones” recently released and based on the 2016 Census. The main driver behind this analysis is to determine what modal share of the tranpsort for people who spend their day within Dublin City Centre is by bicycle, and how this compares with the other modes. This is a hugely important topic in the light of all the recent infrastructure projects within Dublin (LUAS Cross City, Bus Rapid Transit Routes) and the ongoing delay in creating a “Liffey Cycle Route”.

Just to be upfront, I am an avid cyclist, with a 30+km commute into the centre of the city, and therefore I have an avid belief that cycling is the foremost way to improve the flow of people within the city. Saying that, I am a firm believer in letting the data tell a story, so I will try to be as objective as possible in this analysis.

Sourcing Data

The data for this analysis is sourced from the website of the Central Statistics Office - Ireland’s agency tasked with keeping track of all the numbers. A census was performed in 2016, and aside from the standard information based on where people reside, detailed information has been released that outlines where people work or attend school. Further information can be found at CSO.

library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(magrittr)
library(sf)
Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3
library(downloader)
library(stringr)
library(purrr)

Attaching package: 㤼㸱purrr㤼㸲

The following object is masked from 㤼㸱package:magrittr㤼㸲:

    set_names
if (!dir.exists("data")) {
  dir.create("data")
}
if (!dir.exists("output")) {
  dir.create("output")
}
if (!dir.exists("plots")) {
  dir.create("plots")
}
if (!file.exists("data/Workplace_Zones_ITM.shp")) {
# download
  downloader::download("http://www.cso.ie/censusfiles/Workplace_Zones_ITM.zip",
                       dest = "data/wz.zip", mode = "wb")
  unzip("data/wz.zip", exdir = "data")
  file.remove("data/wz.zip")
}
if (!file.exists("data/wz.xlsx")) {
# download
  downloader::download("http://www.cso.ie/en/media/csoie/census/census2016/census2016results/saps/Workplace_zones_-_SAPS_2016.xlsx", dest = "data/wz.xlsx", mode = "wb")
}
if (!file.exists("data/wz_lookup.xlsx")) {
# download
  downloader::download("http://www.cso.ie/en/media/csoie/census/census2016/census2016results/saps/Workplace_Zones_SAPs_Theme_breakdown.xlsx", dest = "data/wz_lookup.xlsx", mode = "wb")
}
wp_zones <- st_read("data/Workplace_Zones_ITM.shp")
Reading layer `Workplace_Zones_ITM' from data source `C:\Users\Admin\Documents\R\cso_bike\data\Workplace_Zones_ITM.shp' using driver `ESRI Shapefile'
Simple feature collection with 7219 features and 8 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 417471.5 ymin: 519663.7 xmax: 734481.1 ymax: 966896.3
epsg (SRID):    NA
proj4string:    +proj=tmerc +lat_0=53.5 +lon_0=-8 +k=0.99982 +x_0=600000 +y_0=750000 +ellps=GRS80 +units=m +no_defs
wp_data <- readxl::read_xlsx("data/wz.xlsx")
wp_lookup <- readxl::read_xlsx("data/wz_lookup.xlsx")
wp_lookup <- wp_lookup[-1,-c(1:2,5)]

The entire data for the Republic of Ireland is now downloaded. We need to subset the data for the area we are interested in. This broadly relates to the area within Dublin City Council. The statistics included in this data are reasonably vague - part of this is because the Census is required to be anonymous, but there is also ambigous aspects to how the people working, at school or at home are counted. Again, read the notes in the CSO description of this data to get more insight. I have done my best to intrepret it, and where possible I’ve explained my reasoning..

wp_zones %>% filter(COUNTY == 'DC') -> dub_wz
st_crs(dub_wz) <- 2157
# now lets join the polygons with the data
dub_wz <- dub_wz %>% left_join(wp_data)
Joining, by = "GUID"
Column `GUID` joining factor and character vector, coercing into character vector
# remove spurious data
dub_wz <- dub_wz[,-c(1:7)]
# modal share for transport
#  subset fields
dub_wz %>% select(T11_C1, starts_with('T2')) -> dub_mode
dub_mode[is.na(dub_mode)] <- 0
dub_mode %<>% mutate(prop_cycling = T2_M2 / T2_T) 
dub_mode %<>% mutate(prop_walking = T2_M1 / T2_T)
dub_mode %<>% mutate(prop_bus = T2_M3 / T2_T)
dub_mode %<>% mutate(prop_train = T2_M4 / T2_T)
dub_mode %<>% mutate(prop_motorcycle = T2_M5 / T2_T)
dub_mode %<>% mutate(prop_car = (T2_M5 + T2_M6 + T2_M7) / T2_T)
dub_mode %<>% mutate(prop_other_ns = (T2_M9 + T2_NS) / T2_T)
dub_mode_prop <- dub_mode %>% st_set_geometry(NULL) %>%  round(2)
dub_mode_prop$guid <- 
# calculate prop for journey time
dub_wz %>% select(starts_with('T4')) -> dub_journey_time
Warning in cbind(x[0:(framecol - 1)], cols) :
  number of rows of result is not a multiple of vector length (arg 2)
# calculate time leaving home
dub_wz %>% select(starts_with('T5')) -> dub_leave_home

We now want to have a look at our data. At this moment in time, the best way to view R geospatial data is via the Leaflet web-mapping package, which is a Java based infrastructure. It allows interactive and close examination of the data in a way that would be familiar to anyone using maps on the internet. The geom_sf functions of the ggplot2 universe have not yet reached production standard, and also I had trouble installing them on my machine for some reason….

#first transform our wz data to wgs84, a web friendly projection that'll play nicely with leaflet
dub_mode_wgs84 <- dub_mode %>% st_transform(4326)
library(leaflet)
package 㤼㸱leaflet㤼㸲 was built under R version 3.4.4
#show a map of dublin city centre
d <- leaflet() %>% addTiles(group = "OSM (default)") %>% 
  addProviderTiles(providers$OpenMapSurfer.Grayscale, group = "Gray") %>%
  addProviderTiles(providers$Thunderforest.OpenCycleMap, group = "CycleMap") %>% 
  addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") 
  
  
d %>%  addMarkers(lng= -6.271394, lat=53.345522, popup="The Dublin Quays")

#now we'd like to show the proportional breakdown of cycling mode
# first set up a colour palette - see https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf
# this also very useful - https://rstudio.github.io/leaflet/choropleths.html
pal_cycling <- colorBin(
  palette = "YlOrRd",
  domain = dub_mode_wgs84$prop_cycling)
d_wz_cycling <- d %>% addPolygons(data = dub_mode_wgs84,
                          weight = 0,
                          opacity = 1,
                          color = 'navy',
                          fillOpacity = 0.6,
                          fillColor = ~pal_cycling(prop_cycling))
d_wz_cycling %>% addLegend(pal = pal_cycling, values = dub_mode_wgs84$prop_cycling, position = "bottomright")

pal_car <- colorNumeric(
  palette = "Reds",
  domain = dub_mode_wgs84$prop_car)
d_wz_car <- d %>% addPolygons(data = dub_mode_wgs84,
                          weight = 0,
                          opacity = 1,
                          color = 'red',
                          fillOpacity = 0.6,
                          fillColor = ~pal_car(prop_car))
d_wz_car %>% addLegend(pal = pal_car, values = dub_mode_wgs84$prop_car, position = "bottomright")

# create a colour scheme for proportionality
bins <- c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
pal_prop <- colorBin("YlOrRd", domain = c(0, 1), bins = bins)
# add labels to each polygon
car_labels <- sprintf(
  "%g Motorcycle<br/>%g Car Driver<br/>%g Car Passenger<br/><strong>%g Total</strong>",
  dub_mode_wgs84$T2_M5, dub_mode_wgs84$T2_M6, dub_mode_wgs84$T2_M7, dub_mode_wgs84$T2_T
) %>% lapply(htmltools::HTML)
bike_labels <- sprintf(
  "%g Bicycle<br/><strong>%g Total</strong>",
  dub_mode_wgs84$T2_M2, dub_mode_wgs84$T2_T
) %>% lapply(htmltools::HTML)
d_wz_car_cycling <- d %>% 
  addPolygons(data = dub_mode_wgs84,
              group = "Bicycle",
                          weight = 0,
                          opacity = 1,
                          color = 'navy',
                          fillOpacity = 0.6,
                          fillColor = ~pal_prop(prop_cycling),
              label = bike_labels,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal", padding = "3px 8px"),
                textsize = "15px",
                direction = "auto")
              ) %>% 
  addPolygons(data = dub_mode_wgs84,
              group = "Car (Driver and Passenger)",
                          weight = 0,
                          opacity = 1,
                          color = 'red',
                          fillOpacity = 0.6,
                          fillColor = ~pal_prop(prop_car),
              label = car_labels,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal", padding = "3px 8px"),
                textsize = "15px",
                direction = "auto")
              )
d_wz_car_cycling %<>% addLayersControl(
  baseGroups = c("OSM (default)", "Gray", "CycleMap", "Satellite"),
  overlayGroups = c("Bicycle", "Car (Driver and Passenger)"),
  options = layersControlOptions(collapsed = FALSE)
) %>% 
  addLegend(pal = pal_prop, values = bins, position = "bottomright", title = "Proportion of Total Daytime Population")
  
d_wz_car_cycling

NExt on the list: make this map beautiful..! make it useful - able to view cycling proportion with comparative shares of other modes - see what effect a good transport link has on the outcome - see what the distribution of travel durations is

LS0tDQp0aXRsZTogIkJpa2UgVHJhdmVsIER1YmxpbiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgVHJhbnBvcnQgTW9kYWwgU2hhcmUgZm9yIER1YmxpbiBDaXR5IENlbnRyZQ0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyBub3RlYm9vayBpcyBpbnRlbmRlZCBhcyBhIGJyaWVmIGFuYWx5c2lzIG9mIHRoZSBDZW50cmFsIFN0YXRpc3RpY3MgT2ZmaWNlICJXb3JrcGxhY2UgWm9uZXMiIHJlY2VudGx5IHJlbGVhc2VkIGFuZCBiYXNlZCBvbiB0aGUgMjAxNiBDZW5zdXMuIFRoZSBtYWluIGRyaXZlciBiZWhpbmQgdGhpcyBhbmFseXNpcyBpcyB0byBkZXRlcm1pbmUgd2hhdCBtb2RhbCBzaGFyZSBvZiB0aGUgdHJhbnBzb3J0IGZvciBwZW9wbGUgd2hvIHNwZW5kIHRoZWlyIGRheSB3aXRoaW4gRHVibGluIENpdHkgQ2VudHJlIGlzIGJ5IGJpY3ljbGUsIGFuZCBob3cgdGhpcyBjb21wYXJlcyB3aXRoIHRoZSBvdGhlciBtb2Rlcy4gVGhpcyBpcyBhIGh1Z2VseSBpbXBvcnRhbnQgdG9waWMgaW4gdGhlIGxpZ2h0IG9mIGFsbCB0aGUgcmVjZW50IGluZnJhc3RydWN0dXJlIHByb2plY3RzIHdpdGhpbiBEdWJsaW4gKExVQVMgQ3Jvc3MgQ2l0eSwgQnVzIFJhcGlkIFRyYW5zaXQgUm91dGVzKSBhbmQgdGhlIG9uZ29pbmcgZGVsYXkgaW4gY3JlYXRpbmcgYSAiTGlmZmV5IEN5Y2xlIFJvdXRlIi4NCg0KSnVzdCB0byBiZSB1cGZyb250LCBJIGFtIGFuIGF2aWQgY3ljbGlzdCwgd2l0aCBhIDMwK2ttIGNvbW11dGUgaW50byB0aGUgY2VudHJlIG9mIHRoZSBjaXR5LCBhbmQgdGhlcmVmb3JlIEkgaGF2ZSBhbiBhdmlkIGJlbGllZiB0aGF0IGN5Y2xpbmcgaXMgX190aGVfXyBmb3JlbW9zdCB3YXkgdG8gaW1wcm92ZSB0aGUgZmxvdyBvZiBwZW9wbGUgd2l0aGluIHRoZSBjaXR5LiBTYXlpbmcgdGhhdCwgSSBhbSBhIGZpcm0gYmVsaWV2ZXIgaW4gbGV0dGluZyB0aGUgZGF0YSB0ZWxsIGEgc3RvcnksIHNvIEkgd2lsbCB0cnkgdG8gYmUgYXMgb2JqZWN0aXZlIGFzIHBvc3NpYmxlIGluIHRoaXMgYW5hbHlzaXMuDQoNCiMjIFNvdXJjaW5nIERhdGENCg0KVGhlIGRhdGEgZm9yIHRoaXMgYW5hbHlzaXMgaXMgc291cmNlZCBmcm9tIHRoZSB3ZWJzaXRlIG9mIHRoZSBDZW50cmFsIFN0YXRpc3RpY3MgT2ZmaWNlIC0gSXJlbGFuZCdzIGFnZW5jeSB0YXNrZWQgd2l0aCBrZWVwaW5nIHRyYWNrIG9mIGFsbCB0aGUgbnVtYmVycy4gQSBjZW5zdXMgd2FzIHBlcmZvcm1lZCBpbiAyMDE2LCBhbmQgYXNpZGUgZnJvbSB0aGUgc3RhbmRhcmQgaW5mb3JtYXRpb24gYmFzZWQgb24gd2hlcmUgcGVvcGxlIHJlc2lkZSwgZGV0YWlsZWQgaW5mb3JtYXRpb24gaGFzIGJlZW4gcmVsZWFzZWQgdGhhdCBvdXRsaW5lcyB3aGVyZSBwZW9wbGUgd29yayBvciBhdHRlbmQgc2Nob29sLiBGdXJ0aGVyIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBhdCBbQ1NPXShodHRwOi8vd3d3LmNzby5pZS9lbi9jZW5zdXMvY2Vuc3VzMjAxNnJlcG9ydHMvY2Vuc3VzMjAxNnNtYWxsYXJlYXBvcHVsYXRpb25zdGF0aXN0aWNzLykuIA0KDQpgYGB7ciBzZXR1cH0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShzZikNCmxpYnJhcnkoZG93bmxvYWRlcikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkocHVycnIpDQpgYGANCg0KDQpgYGB7ciBsb2FkIGRhdGEsIGNhY2hlPVRSVUV9DQppZiAoIWRpci5leGlzdHMoImRhdGEiKSkgew0KICBkaXIuY3JlYXRlKCJkYXRhIikNCn0NCg0KaWYgKCFkaXIuZXhpc3RzKCJvdXRwdXQiKSkgew0KICBkaXIuY3JlYXRlKCJvdXRwdXQiKQ0KfQ0KDQppZiAoIWRpci5leGlzdHMoInBsb3RzIikpIHsNCiAgZGlyLmNyZWF0ZSgicGxvdHMiKQ0KfQ0KDQppZiAoIWZpbGUuZXhpc3RzKCJkYXRhL1dvcmtwbGFjZV9ab25lc19JVE0uc2hwIikpIHsNCiMgZG93bmxvYWQNCiAgZG93bmxvYWRlcjo6ZG93bmxvYWQoImh0dHA6Ly93d3cuY3NvLmllL2NlbnN1c2ZpbGVzL1dvcmtwbGFjZV9ab25lc19JVE0uemlwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVzdCA9ICJkYXRhL3d6LnppcCIsIG1vZGUgPSAid2IiKQ0KICB1bnppcCgiZGF0YS93ei56aXAiLCBleGRpciA9ICJkYXRhIikNCiAgZmlsZS5yZW1vdmUoImRhdGEvd3ouemlwIikNCn0NCg0KaWYgKCFmaWxlLmV4aXN0cygiZGF0YS93ei54bHN4IikpIHsNCiMgZG93bmxvYWQNCiAgZG93bmxvYWRlcjo6ZG93bmxvYWQoImh0dHA6Ly93d3cuY3NvLmllL2VuL21lZGlhL2Nzb2llL2NlbnN1cy9jZW5zdXMyMDE2L2NlbnN1czIwMTZyZXN1bHRzL3NhcHMvV29ya3BsYWNlX3pvbmVzXy1fU0FQU18yMDE2Lnhsc3giLCBkZXN0ID0gImRhdGEvd3oueGxzeCIsIG1vZGUgPSAid2IiKQ0KfQ0KDQppZiAoIWZpbGUuZXhpc3RzKCJkYXRhL3d6X2xvb2t1cC54bHN4IikpIHsNCiMgZG93bmxvYWQNCiAgZG93bmxvYWRlcjo6ZG93bmxvYWQoImh0dHA6Ly93d3cuY3NvLmllL2VuL21lZGlhL2Nzb2llL2NlbnN1cy9jZW5zdXMyMDE2L2NlbnN1czIwMTZyZXN1bHRzL3NhcHMvV29ya3BsYWNlX1pvbmVzX1NBUHNfVGhlbWVfYnJlYWtkb3duLnhsc3giLCBkZXN0ID0gImRhdGEvd3pfbG9va3VwLnhsc3giLCBtb2RlID0gIndiIikNCn0NCg0Kd3Bfem9uZXMgPC0gc3RfcmVhZCgiZGF0YS9Xb3JrcGxhY2VfWm9uZXNfSVRNLnNocCIpDQoNCndwX2RhdGEgPC0gcmVhZHhsOjpyZWFkX3hsc3goImRhdGEvd3oueGxzeCIpDQoNCndwX2xvb2t1cCA8LSByZWFkeGw6OnJlYWRfeGxzeCgiZGF0YS93el9sb29rdXAueGxzeCIpDQoNCndwX2xvb2t1cCA8LSB3cF9sb29rdXBbLTEsLWMoMToyLDUpXQ0KYGBgDQoNClRoZSBlbnRpcmUgZGF0YSBmb3IgdGhlIFJlcHVibGljIG9mIElyZWxhbmQgaXMgbm93IGRvd25sb2FkZWQuIFdlIG5lZWQgdG8gc3Vic2V0IHRoZSBkYXRhIGZvciB0aGUgYXJlYSB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4gVGhpcyBicm9hZGx5IHJlbGF0ZXMgdG8gdGhlIGFyZWEgd2l0aGluIER1YmxpbiBDaXR5IENvdW5jaWwuIFRoZSBzdGF0aXN0aWNzIGluY2x1ZGVkIGluIHRoaXMgZGF0YSBhcmUgcmVhc29uYWJseSB2YWd1ZSAtIHBhcnQgb2YgdGhpcyBpcyBiZWNhdXNlIHRoZSBDZW5zdXMgaXMgcmVxdWlyZWQgdG8gYmUgYW5vbnltb3VzLCBidXQgdGhlcmUgaXMgYWxzbyBhbWJpZ291cyBhc3BlY3RzIHRvIGhvdyB0aGUgcGVvcGxlIHdvcmtpbmcsIGF0IHNjaG9vbCBvciBhdCBob21lIGFyZSBjb3VudGVkLiBBZ2FpbiwgcmVhZCB0aGUgbm90ZXMgaW4gdGhlIENTTyBkZXNjcmlwdGlvbiBvZiB0aGlzIGRhdGEgdG8gZ2V0IG1vcmUgaW5zaWdodC4gSSBoYXZlIGRvbmUgbXkgYmVzdCB0byBpbnRyZXByZXQgaXQsIGFuZCB3aGVyZSBwb3NzaWJsZSBJJ3ZlIGV4cGxhaW5lZCBteSByZWFzb25pbmcuLg0KDQpgYGB7ciBwcmUtcHJvY2Vzc2luZ30NCndwX3pvbmVzICU+JSBmaWx0ZXIoQ09VTlRZID09ICdEQycpIC0+IGR1Yl93eg0Kc3RfY3JzKGR1Yl93eikgPC0gMjE1Nw0KDQojIG5vdyBsZXRzIGpvaW4gdGhlIHBvbHlnb25zIHdpdGggdGhlIGRhdGENCmR1Yl93eiA8LSBkdWJfd3ogJT4lIGxlZnRfam9pbih3cF9kYXRhKQ0KIyByZW1vdmUgc3B1cmlvdXMgZGF0YQ0KZHViX3d6IDwtIGR1Yl93elssLWMoMTo3KV0NCg0KIyBtb2RhbCBzaGFyZSBmb3IgdHJhbnNwb3J0DQojICBzdWJzZXQgZmllbGRzDQpkdWJfd3ogJT4lIHNlbGVjdChUMTFfQzEsIHN0YXJ0c193aXRoKCdUMicpKSAtPiBkdWJfbW9kZQ0KZHViX21vZGVbaXMubmEoZHViX21vZGUpXSA8LSAwDQpkdWJfbW9kZSAlPD4lIG11dGF0ZShwcm9wX2N5Y2xpbmcgPSBUMl9NMiAvIFQyX1QpIA0KZHViX21vZGUgJTw+JSBtdXRhdGUocHJvcF93YWxraW5nID0gVDJfTTEgLyBUMl9UKQ0KZHViX21vZGUgJTw+JSBtdXRhdGUocHJvcF9idXMgPSBUMl9NMyAvIFQyX1QpDQpkdWJfbW9kZSAlPD4lIG11dGF0ZShwcm9wX3RyYWluID0gVDJfTTQgLyBUMl9UKQ0KZHViX21vZGUgJTw+JSBtdXRhdGUocHJvcF9tb3RvcmN5Y2xlID0gVDJfTTUgLyBUMl9UKQ0KZHViX21vZGUgJTw+JSBtdXRhdGUocHJvcF9jYXIgPSAoVDJfTTUgKyBUMl9NNiArIFQyX003KSAvIFQyX1QpDQpkdWJfbW9kZSAlPD4lIG11dGF0ZShwcm9wX290aGVyX25zID0gKFQyX005ICsgVDJfTlMpIC8gVDJfVCkNCg0KZHViX21vZGVfcHJvcCA8LSBkdWJfbW9kZSAlPiUgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JSAgcm91bmQoMikNCmR1Yl9tb2RlX3Byb3AkZ3VpZCA8LSANCiMgY2FsY3VsYXRlIHByb3AgZm9yIGpvdXJuZXkgdGltZQ0KDQpkdWJfd3ogJT4lIHNlbGVjdChzdGFydHNfd2l0aCgnVDQnKSkgLT4gZHViX2pvdXJuZXlfdGltZQ0KDQojIGNhbGN1bGF0ZSB0aW1lIGxlYXZpbmcgaG9tZQ0KZHViX3d6ICU+JSBzZWxlY3Qoc3RhcnRzX3dpdGgoJ1Q1JykpIC0+IGR1Yl9sZWF2ZV9ob21lDQpgYGANCg0KV2Ugbm93IHdhbnQgdG8gaGF2ZSBhIGxvb2sgYXQgb3VyIGRhdGEuIEF0IHRoaXMgbW9tZW50IGluIHRpbWUsIHRoZSBiZXN0IHdheSB0byB2aWV3IFIgZ2Vvc3BhdGlhbCBkYXRhIGlzIHZpYSB0aGUgTGVhZmxldCB3ZWItbWFwcGluZyBwYWNrYWdlLCB3aGljaCBpcyBhIEphdmEgYmFzZWQgaW5mcmFzdHJ1Y3R1cmUuIEl0IGFsbG93cyBpbnRlcmFjdGl2ZSBhbmQgY2xvc2UgZXhhbWluYXRpb24gb2YgdGhlIGRhdGEgaW4gYSB3YXkgdGhhdCB3b3VsZCBiZSBmYW1pbGlhciB0byBhbnlvbmUgdXNpbmcgbWFwcyBvbiB0aGUgaW50ZXJuZXQuIFRoZSBnZW9tX3NmIGZ1bmN0aW9ucyBvZiB0aGUgZ2dwbG90MiB1bml2ZXJzZSBoYXZlIG5vdCB5ZXQgcmVhY2hlZCBwcm9kdWN0aW9uIHN0YW5kYXJkLCBhbmQgYWxzbyBJIGhhZCB0cm91YmxlIGluc3RhbGxpbmcgdGhlbSBvbiBteSBtYWNoaW5lIGZvciBzb21lIHJlYXNvbi4uLi4NCg0KYGBge3IgbWFwcGluZ30NCg0KI2ZpcnN0IHRyYW5zZm9ybSBvdXIgd3ogZGF0YSB0byB3Z3M4NCwgYSB3ZWIgZnJpZW5kbHkgcHJvamVjdGlvbiB0aGF0J2xsIHBsYXkgbmljZWx5IHdpdGggbGVhZmxldA0KZHViX21vZGVfd2dzODQgPC0gZHViX21vZGUgJT4lIHN0X3RyYW5zZm9ybSg0MzI2KQ0KDQpsaWJyYXJ5KGxlYWZsZXQpDQoNCiNzaG93IGEgbWFwIG9mIGR1YmxpbiBjaXR5IGNlbnRyZQ0KZCA8LSBsZWFmbGV0KCkgJT4lIGFkZFRpbGVzKGdyb3VwID0gIk9TTSAoZGVmYXVsdCkiKSAlPiUgDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJE9wZW5NYXBTdXJmZXIuR3JheXNjYWxlLCBncm91cCA9ICJHcmF5IikgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFRodW5kZXJmb3Jlc3QuT3BlbkN5Y2xlTWFwLCBncm91cCA9ICJDeWNsZU1hcCIpICU+JSANCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkRXNyaS5Xb3JsZEltYWdlcnksIGdyb3VwID0gIlNhdGVsbGl0ZSIpIA0KICANCiAgDQpkICU+JSAgYWRkTWFya2Vycyhsbmc9IC02LjI3MTM5NCwgbGF0PTUzLjM0NTUyMiwgcG9wdXA9IlRoZSBEdWJsaW4gUXVheXMiKQ0KDQojbm93IHdlJ2QgbGlrZSB0byBzaG93IHRoZSBwcm9wb3J0aW9uYWwgYnJlYWtkb3duIG9mIGN5Y2xpbmcgbW9kZQ0KIyBmaXJzdCBzZXQgdXAgYSBjb2xvdXIgcGFsZXR0ZSAtIHNlZSBodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9+ZnJhemllci9SU3BhdGlhbEd1aWRlcy9jb2xvclBhbGV0dGVDaGVhdHNoZWV0LnBkZg0KIyB0aGlzIGFsc28gdmVyeSB1c2VmdWwgLSBodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvY2hvcm9wbGV0aHMuaHRtbA0KDQpwYWxfY3ljbGluZyA8LSBjb2xvckJpbigNCiAgcGFsZXR0ZSA9ICJZbE9yUmQiLA0KICBkb21haW4gPSBkdWJfbW9kZV93Z3M4NCRwcm9wX2N5Y2xpbmcpDQoNCmRfd3pfY3ljbGluZyA8LSBkICU+JSBhZGRQb2x5Z29ucyhkYXRhID0gZHViX21vZGVfd2dzODQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICduYXZ5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjYsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wYWxfY3ljbGluZyhwcm9wX2N5Y2xpbmcpKQ0KZF93el9jeWNsaW5nICU+JSBhZGRMZWdlbmQocGFsID0gcGFsX2N5Y2xpbmcsIHZhbHVlcyA9IGR1Yl9tb2RlX3dnczg0JHByb3BfY3ljbGluZywgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiKQ0KDQpwYWxfY2FyIDwtIGNvbG9yTnVtZXJpYygNCiAgcGFsZXR0ZSA9ICJSZWRzIiwNCiAgZG9tYWluID0gZHViX21vZGVfd2dzODQkcHJvcF9jYXIpDQoNCmRfd3pfY2FyIDwtIGQgJT4lIGFkZFBvbHlnb25zKGRhdGEgPSBkdWJfbW9kZV93Z3M4NCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC42LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGFsX2Nhcihwcm9wX2NhcikpDQpkX3d6X2NhciAlPiUgYWRkTGVnZW5kKHBhbCA9IHBhbF9jYXIsIHZhbHVlcyA9IGR1Yl9tb2RlX3dnczg0JHByb3BfY2FyLCBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIpDQoNCiMgY3JlYXRlIGEgY29sb3VyIHNjaGVtZSBmb3IgcHJvcG9ydGlvbmFsaXR5DQoNCmJpbnMgPC0gYygwLCAwLjEsIDAuMiwgMC4zLCAwLjQsIDAuNSwgMC42LCAwLjcsIDAuOCwgMC45KQ0KcGFsX3Byb3AgPC0gY29sb3JCaW4oIllsT3JSZCIsIGRvbWFpbiA9IGMoMCwgMSksIGJpbnMgPSBiaW5zKQ0KDQojIGFkZCBsYWJlbHMgdG8gZWFjaCBwb2x5Z29uDQoNCmNhcl9sYWJlbHMgPC0gc3ByaW50ZigNCiAgIiVnIE1vdG9yY3ljbGU8YnIvPiVnIENhciBEcml2ZXI8YnIvPiVnIENhciBQYXNzZW5nZXI8YnIvPjxzdHJvbmc+JWcgVG90YWw8L3N0cm9uZz4iLA0KICBkdWJfbW9kZV93Z3M4NCRUMl9NNSwgZHViX21vZGVfd2dzODQkVDJfTTYsIGR1Yl9tb2RlX3dnczg0JFQyX003LCBkdWJfbW9kZV93Z3M4NCRUMl9UDQopICU+JSBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQ0KDQpiaWtlX2xhYmVscyA8LSBzcHJpbnRmKA0KICAiJWcgQmljeWNsZTxici8+PHN0cm9uZz4lZyBUb3RhbDwvc3Ryb25nPiIsDQogIGR1Yl9tb2RlX3dnczg0JFQyX00yLCBkdWJfbW9kZV93Z3M4NCRUMl9UDQopICU+JSBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQ0KDQoNCg0KZF93el9jYXJfY3ljbGluZyA8LSBkICU+JSANCiAgYWRkUG9seWdvbnMoZGF0YSA9IGR1Yl9tb2RlX3dnczg0LA0KICAgICAgICAgICAgICBncm91cCA9ICJCaWN5Y2xlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ25hdnknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuNiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnBhbF9wcm9wKHByb3BfY3ljbGluZyksDQogICAgICAgICAgICAgIGxhYmVsID0gYmlrZV9sYWJlbHMsDQogICAgICAgICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucygNCiAgICAgICAgICAgICAgICBzdHlsZSA9IGxpc3QoImZvbnQtd2VpZ2h0IiA9ICJub3JtYWwiLCBwYWRkaW5nID0gIjNweCA4cHgiKSwNCiAgICAgICAgICAgICAgICB0ZXh0c2l6ZSA9ICIxNXB4IiwNCiAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYXV0byIpDQogICAgICAgICAgICAgICkgJT4lIA0KICBhZGRQb2x5Z29ucyhkYXRhID0gZHViX21vZGVfd2dzODQsDQogICAgICAgICAgICAgIGdyb3VwID0gIkNhciAoRHJpdmVyIGFuZCBQYXNzZW5nZXIpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC42LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGFsX3Byb3AocHJvcF9jYXIpLA0KICAgICAgICAgICAgICBsYWJlbCA9IGNhcl9sYWJlbHMsDQogICAgICAgICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucygNCiAgICAgICAgICAgICAgICBzdHlsZSA9IGxpc3QoImZvbnQtd2VpZ2h0IiA9ICJub3JtYWwiLCBwYWRkaW5nID0gIjNweCA4cHgiKSwNCiAgICAgICAgICAgICAgICB0ZXh0c2l6ZSA9ICIxNXB4IiwNCiAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYXV0byIpDQogICAgICAgICAgICAgICkNCmRfd3pfY2FyX2N5Y2xpbmcgJTw+JSBhZGRMYXllcnNDb250cm9sKA0KICBiYXNlR3JvdXBzID0gYygiT1NNIChkZWZhdWx0KSIsICJHcmF5IiwgIkN5Y2xlTWFwIiwgIlNhdGVsbGl0ZSIpLA0KICBvdmVybGF5R3JvdXBzID0gYygiQmljeWNsZSIsICJDYXIgKERyaXZlciBhbmQgUGFzc2VuZ2VyKSIpLA0KICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpDQopICU+JSANCiAgYWRkTGVnZW5kKHBhbCA9IHBhbF9wcm9wLCB2YWx1ZXMgPSBiaW5zLCBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsIHRpdGxlID0gIlByb3BvcnRpb24gb2YgVG90YWwgRGF5dGltZSBQb3B1bGF0aW9uIikNCiAgDQpkX3d6X2Nhcl9jeWNsaW5nDQoNCmBgYA0KDQpORXh0IG9uIHRoZSBsaXN0OiBtYWtlIHRoaXMgbWFwIGJlYXV0aWZ1bC4uISBtYWtlIGl0IHVzZWZ1bCAtIGFibGUgdG8gdmlldyBjeWNsaW5nIHByb3BvcnRpb24gd2l0aCBjb21wYXJhdGl2ZSBzaGFyZXMgb2Ygb3RoZXIgbW9kZXMgLSBzZWUgd2hhdCBlZmZlY3QgYSBnb29kIHRyYW5zcG9ydCBsaW5rIGhhcyBvbiB0aGUgb3V0Y29tZSAtIHNlZSB3aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdHJhdmVsIGR1cmF0aW9ucyBpcyANCg0KDQo=